#include "../helper.h"
#include <hpsocket/HPSocket.h>

class CListenerImpl : public CTcpAgentListener
{

public:
    virtual EnHandleResult OnPrepareConnect(ITcpAgent* pSender, CONNID dwConnID, SOCKET socket) override
    {
        return HR_OK;
    }

    virtual EnHandleResult OnConnect(ITcpAgent* pSender, CONNID dwConnID) override
    {
        TCHAR szAddress[50];
        int iAddressLen = sizeof(szAddress) / sizeof(TCHAR);
        USHORT usPort;

        pSender->GetRemoteAddress(dwConnID, szAddress, iAddressLen, usPort);

        ::PostOnConnect2(dwConnID, szAddress, usPort);

        return HR_OK;
    }

    virtual EnHandleResult OnHandShake(ITcpAgent* pSender, CONNID dwConnID) override
    {
        return HR_OK;
    }

    virtual EnHandleResult OnReceive(ITcpAgent* pSender, CONNID dwConnID, const BYTE* pData, int iLength) override
    {
        ::PostOnReceive(dwConnID, pData, iLength);
        return HR_OK;
    }

    virtual EnHandleResult OnSend(ITcpAgent* pSender, CONNID dwConnID, const BYTE* pData, int iLength) override
    {
        ::PostOnSend(dwConnID, pData, iLength);
        return HR_OK;
    }

    virtual EnHandleResult OnClose(ITcpAgent* pSender, CONNID dwConnID, EnSocketOperation enOperation, int iErrorCode) override
    {
        iErrorCode == SE_OK ? ::PostOnClose(dwConnID) :
        ::PostOnError(dwConnID, enOperation, iErrorCode);

        return HR_OK;
    }

    virtual EnHandleResult OnShutdown(ITcpAgent* pSender) override
    {
        ::PostOnShutdown();
        return HR_OK;
    }

};

CListenerImpl s_listener;
CTcpPackAgentPtr s_agent(&s_listener);

void OnCmdStart(CCommandParser* pParser)
{
    if(s_agent->Start(g_app_arg.bind_addr, g_app_arg.async))
        ::LogAgentStart(g_app_arg.bind_addr, g_app_arg.async);
    else
        ::LogAgentStartFail(s_agent->GetLastError(), s_agent->GetLastErrorDesc());
}

void OnCmdStop(CCommandParser* pParser)
{
    if(s_agent->Stop())
        ::LogAgentStop();
    else
        ::LogAgentStopFail(s_agent->GetLastError(), s_agent->GetLastErrorDesc());
}

void OnCmdStatus(CCommandParser* pParser)
{
    pParser->PrintStatus(s_agent->GetState());
}

void OnCmdConnect(CCommandParser* pParser)
{
    ::LogConnect(pParser->m_strRemoteAddr, pParser->m_usRemotePort);

    if(!s_agent->Connect(pParser->m_strRemoteAddr, pParser->m_usRemotePort))
        ::LogConnectFail(::GetLastError(), ::GetLastErrorStr());
}

void OnCmdSend(CCommandParser* pParser)
{
    if(s_agent->Send(pParser->m_dwConnID, (LPBYTE)(LPCTSTR)pParser->m_strMessage, pParser->m_strMessage.GetLength()))
    ::LogSend(pParser->m_dwConnID, pParser->m_strMessage);
    else
    ::LogSendFail(pParser->m_dwConnID, ::GetLastError(), ::GetLastErrorStr());
}

void OnCmdPause(CCommandParser* pParser)
{
    if(s_agent->PauseReceive(pParser->m_dwConnID, pParser->m_bFlag))
        ::LogPause(pParser->m_dwConnID, pParser->m_bFlag);
    else
        ::LogPauseFail(pParser->m_dwConnID, pParser->m_bFlag);
}

void OnCmdKick(CCommandParser* pParser)
{
    if(s_agent->Disconnect(pParser->m_dwConnID, pParser->m_bFlag))
        ::LogDisconnect2(pParser->m_dwConnID, pParser->m_bFlag);
    else
        ::LogDisconnectFail2(pParser->m_dwConnID, pParser->m_bFlag);
}

void OnCmdKickLong(CCommandParser* pParser)
{
    if(s_agent->DisconnectLongConnections(pParser->m_dwSeconds * 1000, pParser->m_bFlag))
        ::LogDisconnectLong(pParser->m_dwSeconds, pParser->m_bFlag);
    else
        ::LogDisconnectFailLong(pParser->m_dwSeconds, pParser->m_bFlag);
}

void OnCmdKickSilence(CCommandParser* pParser)
{
    if(s_agent->DisconnectSilenceConnections(pParser->m_dwSeconds * 1000, pParser->m_bFlag))
        ::LogDisconnectLong(pParser->m_dwSeconds, pParser->m_bFlag);
    else
        ::LogDisconnectFailLong(pParser->m_dwSeconds, pParser->m_bFlag);
}

int main(int argc, char* const argv[])
{
    CTermAttrInitializer term_attr;
    CAppSignalHandler s_signal_handler({SIGTTOU, SIGINT});

    g_app_arg.ParseArgs(argc, argv);

    s_agent->SetMaxPackSize(0x7FF);
    s_agent->SetPackHeaderFlag(0x169);
    s_agent->SetKeepAliveTime(g_app_arg.keep_alive ? TCP_KEEPALIVE_TIME : 0);

    CCommandParser::CMD_FUNC fnCmds[CCommandParser::CT_MAX] = {0};

    fnCmds[CCommandParser::CT_START]	= OnCmdStart;
    fnCmds[CCommandParser::CT_STOP]		= OnCmdStop;
    fnCmds[CCommandParser::CT_STATUS]	= OnCmdStatus;
    fnCmds[CCommandParser::CT_CONNECT]	= OnCmdConnect;
    fnCmds[CCommandParser::CT_SEND]		= OnCmdSend;
    fnCmds[CCommandParser::CT_PAUSE]	= OnCmdPause;
    fnCmds[CCommandParser::CT_KICK]		= OnCmdKick;
    fnCmds[CCommandParser::CT_KICK_L]	= OnCmdKickLong;
    fnCmds[CCommandParser::CT_KICK_S]	= OnCmdKickSilence;

    CCommandParser s_cmd_parser(CCommandParser::AT_AGENT, fnCmds);
    s_cmd_parser.Run();

    return EXIT_CODE_OK;
}
